package com.wayne.oauth2.configuration;

import com.wayne.oauth2.provider.EmailPasswordGranter;
import com.wayne.oauth2.provider.WayneTokenEnhancer;
import com.wayne.oauth2.service.GranterLoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeTokenGranter;
import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author BinWei
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    @Autowired
    private ClientDetailsService jdbcClientDetailsService;
    @Autowired
    private AuthorizationCodeServices authorizationCodeServices;
    @Autowired
    private ApprovalStore approvalStore;
    @Autowired
    private TokenStore tokenStore;
    @Autowired
    private WayneTokenEnhancer wayneTokenEnhancer;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //Password认证模式需要
        endpoints.authenticationManager(authenticationManager);
        //将token转换为jwt格式
        //endpoints.accessTokenConverter(jwtAccessTokenConverter);
        //将授权码存储到数据库中
        endpoints.authorizationCodeServices(authorizationCodeServices);
        //将授权记录存储到数据库中
        endpoints.approvalStore(approvalStore);
        //将Token记录到数据库中
        endpoints.tokenStore(tokenStore);
        /*
         扩展返回信息
         注意：这里要是让wayneTokenEnhancer在jwtTokenConverter前面
         不然使用jwt的话会不成功
        * */
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        ArrayList<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(wayneTokenEnhancer);
        delegates.add(jwtAccessTokenConverter);
        enhancerChain.setTokenEnhancers(delegates);
        endpoints.tokenEnhancer(enhancerChain);
        //自定义登录方式
        List<TokenGranter> tokenGranters = getTokenGranters(endpoints.getAuthorizationCodeServices(), endpoints.getTokenStore(), endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory());
        endpoints.tokenGranter(new CompositeTokenGranter(tokenGranters));
    }

    @Autowired
    private GranterLoginService granterLoginService;

    private List<TokenGranter> getTokenGranters(AuthorizationCodeServices authorizationCodeServices, TokenStore tokenStore, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
        return new ArrayList<>(Arrays.asList(
                //默认授权码模式登录
                new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetailsService, requestFactory),
                //默认密码模式登录
                new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory),
                //默认client_credentials模式登录
                new ClientCredentialsTokenGranter(tokenServices, clientDetailsService, requestFactory),
                //自定义邮箱/密码登录
                new EmailPasswordGranter(granterLoginService, tokenServices, clientDetailsService, requestFactory)
        ));
    }

    /**
     * 配置客户端信息(这里配置的不是登录用户信息 而是可以访问系统的客户端)
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(jdbcClientDetailsService);
    }

    /**
     * 开启表单认证 支持外部接口认证
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //开启表单认证，主要是让/oauth/token支持client_id以及client_secret作登录认证
        security.allowFormAuthenticationForClients()
                //开启 /oauth/token_key 验证端口无权限访问
                .tokenKeyAccess("permitAll()")
                //开启 /oauth/check_token 验证端口无权限访问
                .checkTokenAccess("permitAll()");
    }

}